package com.yx.frame.jdbc.pool;

import java.util.*;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.yx.frame.log.LogFactory;
import com.yx.frame.log.LogWriter;

public final class JPool extends Thread implements Pool {

	private String JDBCDriver = "";

	private String JDBCUrl = "";

	private String JDBCUser = "";

	private String JDBCPassword = "";

	private String name;

	private int poolSize;

	private int bakPoolSize;

	private int usedConnection = 0;
	
	private int maxConn;

	public int requestConnection = 0;
	
	private int getConnTimeout;

	private java.util.Vector dbPool;

	private long MaxRefreshTime = 10000;

	private long refreshTime = 0;

	private boolean doPoolEmpty = false;

	boolean isEncode = true;

	int maxQueryRowCountList = 5000;

	boolean isDefaultDatabasePool = false;
	
	public JPool(String aname, String JDBCDriver, String JDBCUrl,
			String JDBCUser, String JDBCPassword, int apoolSize, int amaxConn,
			int agetConnTimeout,int maxQueryRowCountList,int maxIdleTime,int acquireIncrement) throws Exception {

		this.name = aname;
		this.JDBCDriver = JDBCDriver;
		this.JDBCUrl = JDBCUrl;
		this.JDBCUser = JDBCUser;
		this.JDBCPassword = JDBCPassword;
		this.maxQueryRowCountList = maxQueryRowCountList;
		this.poolSize = apoolSize > 0 ? apoolSize : 2; 
		this.maxConn = amaxConn > 0 ? amaxConn : 0; 
		this.getConnTimeout = agetConnTimeout > 0 ? agetConnTimeout : 5;
		this.bakPoolSize = poolSize;
		dbPool = new java.util.Vector();
		// init pool
		try {
			fillPool(poolSize);
		} catch (Exception e) {
			throw new Exception("初始化数据库连接池"+ name +"出错:" + e);
		}
		this.start();
		log(LogWriter.lOG_INFO,"数据库连接池"+ name +"成功启动！" );

	}

	public boolean isEncode() {
		return isEncode;
	}

	public int getMaxQueryRowCount() {
		return maxQueryRowCountList;
	}

	public boolean isDefaultDatabasePool() {
		return isDefaultDatabasePool;
	}

	public String getJndiName() {
		return name;
	}

	/**
	 * Brings the pool to the given size
	 * 
	 * @param size
	 *            a given size
	 */
	private synchronized void fillPool(int size) throws Exception {
		while (dbPool.size() < size) {
			PConnection co = null;
			try {
				co = new PConnection(this.name,this.JDBCDriver, this.JDBCUrl,
						this.JDBCUser, this.JDBCPassword);
			} catch (Exception e) {
				throw new Exception("创建数据库连接对象出错：" + e);
			}
			co.inUse = false;
			logLastAccess(co);
			dbPool.addElement(co);
		}
		log(LogWriter.lOG_DEBUG, "数据库连接数量：" + dbPool.size());
	}

	/**
	 * getConnection
	 */
	public PConnection getConnection() throws Exception {		
		synchronized (this) {
			requestConnection++;
		}
		try {
			PConnection c = getConnection(getConnTimeout * 1000);
			synchronized (this) {
				requestConnection--;
			}
			return c;
		} catch (Exception e) {
			synchronized (this) {
				requestConnection--;
			}
			throw e;
		}
	}

	/**
	 * getConnection & wait a timeout
	 */
	private synchronized PConnection getConnection(long timeout)
			throws Exception {		
		PConnection co = null;
		if (dbPool == null) {
			log(LogWriter.lOG_DEBUG, "ConnectionPool(" + name + ") is null");
			throw new Exception("ConnectionPool(" + name + ") is null");
			// return database;
		}
		long startTime = System.currentTimeMillis();
		long remaining = timeout;

		while ((co = getPooledConnection()) == null) {
			try {				
				wait(1000);
			} catch (InterruptedException e) {
			}
			remaining = timeout - (System.currentTimeMillis() - startTime);
			if (remaining <= 0) {
				
				throw new Exception("getConnection timeout");
			}
		}
		if (co != null) {
			
			co.inUse = true;
			// co.user = user;
			co.useCount++;
			logLastAccess(co);

		}
		return co;
	}

	public synchronized Vector getConnListFromPool() {
		Vector v = new Vector();
		Enumeration enums = dbPool.elements();
		while (enums.hasMoreElements()) {
			v.addElement(enums.nextElement());
		}
		return v;
	}

	public synchronized int getMaxConn() {
		return maxConn;
	}

	/**
	 * get a Database object from the pool
	 */
	private synchronized PConnection getPooledConnection() throws Exception {
		PConnection connectionObject = null;
		int size = dbPool.size();
		int index = -1;
		int cc = -1;
		for (int i = 0; i < size; i++) {
			PConnection co = (PConnection) dbPool.elementAt(i);
			if (!co.inUse) {
				if (co.isConnected()) {
					if (cc == -1) {
						cc = co.useCount;
						index = i;
					}
					if (co.useCount < cc) {
						cc = co.useCount;
						index = i;
					}
					if (co.useCount == 0) {
						break;
					}
				} else {
					removeFromPool(i);
					i--;
					size--;
				}
			}
		}
		if (index != -1) {
			connectionObject = (PConnection) dbPool.elementAt(index);
		}
		if (connectionObject == null) {
			notifyAll(); // new a connection
		} else {
			usedConnection++;
		}
		return connectionObject;
	}
	
	public synchronized void freeConnection(PConnection db) {
		
		int index = indexOf(db);
		if (index != -1) {
			PConnection co = (PConnection) dbPool.elementAt(index);
			if (co.inUse) {
				usedConnection--;
			}
			if (!db.isConnected()) {				
				removeFromPool(index);				
			} else {
				logLastAccess(co);
				co.inUse = false;
			}
		}

	}

	/**
	 * remove a connectionObject object form the pool
	 * 
	 * @param index
	 */
	private synchronized void removeFromPool(int index) {

		// make sure index and pool are valid
		if (dbPool != null) {
			if (index < dbPool.size()) {
				PConnection co = (PConnection) dbPool.elementAt(index);
				String ms = "";
				if (co.isConnected()) {
					ms = "数据库连接正常";
				} else {
					ms = "数据库连接断开";
				}
				close(co);
				dbPool.removeElementAt(index);
				log(LogWriter.lOG_DEBUG, ms);
			}
		}
	}

	/**
	 * close connection and destroy ConnectionObject
	 * 
	 * @param ConnectionObject
	 */
	private void close(PConnection co) {
		if (co != null) {
			co.disConnect();
		}
	}

	/**
	 * find a ConnectionObject from the pool
	 * 
	 * @param db
	 *            a Database object
	 */
	private int indexOf(PConnection db) {
		int index = -1;
		if (db != null && dbPool != null) {
			for (int i = 0; i < dbPool.size(); i++) {
				PConnection co = (PConnection) dbPool.elementAt(i);
				if (co.equals(db)) {
					index = i;
					break;
				}
			}
		}
		return index;
	}

	/**
	 * close ConnectionPool and release all resourse
	 */
	public synchronized void destroy() {
		// clear the pool
		if (dbPool != null) {
			for (int i = 0; i < dbPool.size(); i++) {
				close((PConnection) dbPool.elementAt(i));
			}
			dbPool.removeAllElements();
			dbPool = null;
		}
	}

	/**
	 * write log information to the log file
	 * 
	 * @param msg
	 *            log information
	 */
	LogWriter log = LogFactory.getInstance().getLogWriter();

	private void log(int level, String msg) {
		log.write(level, null, "数据库连接池", msg);
	}

	/**
	 * log LastAccess time
	 * 
	 * @param connectionObject
	 *            a connectionObject object
	 */
	private void logLastAccess(PConnection connectionObject) {
		if (connectionObject != null) {
			connectionObject.lastAccess = System.currentTimeMillis();
		}
	}

	public synchronized int getMaxConnection() {
		return maxConn;
	}

	public synchronized int getMinConnection() {
		return poolSize;
	}

	public synchronized int getConnectionNumber() {
		return dbPool.size();
	}

	public synchronized int getUsedConnection() {
		return usedConnection;
	}

	public synchronized int getUnusedConnection() {
		return (dbPool.size() - usedConnection);
	}

	PConnection newConnection() throws Exception {
		PConnection co = null;
		try {
			co = new PConnection(name,this.JDBCDriver, this.JDBCUrl,
					this.JDBCUser, this.JDBCPassword);
		} catch (Exception e) {
		}
		if (co == null) {
			log(LogWriter.lOG_DEBUG,"创建数据库连接失败！");
			throw new Exception("创建数据库连接失败！");
		}
		if (!co.isConnected()) {
			close(co);
			log(LogWriter.lOG_DEBUG, "创建的数据库连接没有连接数据库！");
			throw new Exception("创建的数据库连接没有连接数据库！");
		}
		co.inUse = false;
		logLastAccess(co);
		return co;
	}

	synchronized int refreahPool() {
		int freeConn = 0;
		int psize = dbPool.size();
		if (psize == 0) {
			return 0;
		}
		for (int i = psize - 1; i >= 0; i--) {
			PConnection co = (PConnection) dbPool.elementAt(i);
			if (!co.inUse) {
				if (co.isConnected()) {
					freeConn++;
				} else {
					removeFromPool(i);
				}
			}

		}

		return freeConn;
	}

	/**
	 * Start a new thread to fill the connection pool
	 */

	void refreahPool(int size) throws Exception {
		int psize = dbPool.size();
		int num = size - psize;

		if (num == 0) {
			return;
		} else if (num > 0) {
			for (int i = 0; i < num; i++) {
				PConnection co = null;
				try {
					co = newConnection();
				} catch (Exception e) {
					throw e;
				}
				synchronized (this) {
					dbPool.addElement(co);
				}
			}
		} else {
			if (MaxRefreshTime > (System.currentTimeMillis() - refreshTime)) {
				return;
			}
			for (int i = psize - 1; i >= 0; i--) {
				{
					PConnection co = (PConnection) dbPool.elementAt(i);
					if (!co.inUse) {

						removeFromPool(i);
						num++;
					}
					if (num >= 0) {
						break;
					}
				}
				refreshTime = System.currentTimeMillis();
			}

		}
	}

	public void run() {
		while (true) {
			if (doPoolEmpty) {
				poolSize = 0;
			} else {
				poolSize = bakPoolSize;
			}
			int freeConn = refreahPool();
			int pSize = dbPool.size();
			int size = pSize + requestConnection - freeConn;
			if (size < poolSize) {
				size = poolSize;
			}
			if (size > maxConn) {
				size = maxConn;
			}
			try {
				refreahPool(size);
			} catch (Exception e) {
			}
			synchronized (this) {
				try {
					wait(MaxRefreshTime);
				} catch (Exception e) {
				}
			}

		}
	}

	public void setPoolEmpty(boolean isTrue) {
		if (isTrue) {
			doPoolEmpty = true;
		} else {
			doPoolEmpty = false;
		}
	}

	public boolean isSetPoolEmpty() {
		return doPoolEmpty;
	}
	public boolean isFramePool(){
		return true;
	}

	/* (non-Javadoc)
	 * @see com.yx.frame.jdbc.pool.Pool#getDataSource()
	 */
	@Override
	public ComboPooledDataSource getDataSource() {
		return null;
	}
}
